use crate::cljcell::clj_cell::CljCell;
use crate::built_in_functions::my_clojure_interp::{CljFunc, CljRecur, MyClojureInterp, CljFuncParam, CljFuncParamList, MyCljFunc, MyFuncHs, MyCljRecur};
use crate::built_in_functions::util::my_util;
use std::collections::HashMap;
use std::fmt;
use std::rc::Rc;
use std::cell::RefCell;

pub struct MyMath;

impl MyMath {

    pub fn add(c: Rc<CljCell>, c1: Rc<CljCell>) -> Rc<CljCell> {
        match &*c {
            CljCell::IntClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::IntClj(m + m1));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::LongClj((*m as i64) + m1));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj((*m as f32) + m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) + m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::IntClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::LongClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::LongClj(m + (*m1 as i64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::LongClj(m + m1));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj((*m as f32) + m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) + m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::LongClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::DoubleClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m + (*m1 as f64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m + (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m + (*m1 as f64)));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m + m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::DoubleClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::FloatClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::FloatClj(m + (*m1 as f32)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) + (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj(m + m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) + m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::FloatClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::Null => {
                return c1.clone();
            },
            _ => {},
        }
        return Rc::new(CljCell::Null);
    }
    /**
     * 加法
     * */
    pub fn clj_list_addition1(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {
        //let x = Rc::new(CljCell::Null);
        //let mut ptr: *mut CljCell = Rc::into_raw(x) as *mut CljCell;
        //let x = Box::new(CljCell::Null);
        //let mut ptr: *mut CljCell = Box::into_raw(x);
        let mut ptr = Rc::new(CljCell::Null);
        for c in &lst[1..] {
            match &**c {
                CljCell::IntClj(m) => {
                    ptr = MyMath::add(ptr, c.clone());
                },
                CljCell::LongClj(m) => {
                    ptr = MyMath::add(ptr, c.clone());
                },
                CljCell::DoubleClj(m) => {
                    ptr = MyMath::add(ptr, c.clone());
                },
                CljCell::FloatClj(m) => {
                    ptr = MyMath::add(ptr, c.clone());
                },
                CljCell::Symbol(vs) => {
                    let key: Rc<String> = (**vs).clone().into();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(key.as_ref());
                    ptr = MyMath::add(ptr, rccell.clone());
                },
                CljCell::CljList(vs) => {
                    let rs_cell = interp.clj_list(vs, func_hs, clj_recur, rs.clone());
                    ptr = MyMath::add(ptr, rs_cell.clone());
                },
                CljCell::Percent(bpx) => {
                    let vs: &Option<Box<CljCell>> = bpx;
                    let bx: &Box<CljCell> = vs.as_ref().unwrap();
                    let key: String = (*bx).to_string();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(&key);
                    ptr = MyMath::add(ptr, rccell.clone());
                },
                _ => {},
            }
        }
        return ptr;
    }

    pub fn clj_list_addition(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {
        //let ptr:Rc<CljCell>  = MyMath::clj_list_addition1(interp, lst, func_hs, CljRecur, rs.clone());
        //return Rc::try_unwrap(ptr).unwrap();
        return MyMath::clj_list_addition1(interp, lst, func_hs, clj_recur, rs.clone());
    }

    pub fn subtraction(c: Rc<CljCell>, c1: Rc<CljCell>) -> Rc<CljCell> {
        match &*c {
            CljCell::IntClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::IntClj(m - m1));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::LongClj((*m as i64) - m1));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj((*m as f32) - m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) - m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::IntClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::LongClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::LongClj(m - (*m1 as i64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::LongClj(m - m1));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj((*m as f32) - m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) - m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::LongClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::DoubleClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m - (*m1 as f64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m - (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m - (*m1 as f64)));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m - m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::DoubleClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::FloatClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::FloatClj(m - (*m1 as f32)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) - (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj(m - m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) - m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::FloatClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::Null => {
                return c1.clone();
            },
            _ => {},
        }
        return Rc::new(CljCell::Null);
    }
    /**
     * 加法
     * */
    pub fn clj_list_subtraction1(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {

        let mut ptr = Rc::new(CljCell::Null);
        for c in &lst[1..] {
            match &**c {
                CljCell::IntClj(m) => {
                    ptr = MyMath::subtraction(ptr, c.clone());
                },
                CljCell::LongClj(m) => {
                    ptr = MyMath::subtraction(ptr, c.clone());
                },
                CljCell::DoubleClj(m) => {
                    ptr = MyMath::subtraction(ptr, c.clone());
                },
                CljCell::FloatClj(m) => {
                    ptr = MyMath::subtraction(ptr, c.clone());
                },
                CljCell::Symbol(vs) => {
                    let key: Rc<String> = (**vs).clone().into();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(key.as_ref());
                    ptr = MyMath::subtraction(ptr, rccell.clone());
                },
                CljCell::CljList(vs) => {
                    let rs_cell = interp.clj_list(vs, func_hs, clj_recur, rs.clone());
                    ptr = MyMath::add(ptr, rs_cell.clone());
                },
                CljCell::Percent(bpx) => {
                    let vs: &Option<Box<CljCell>> = bpx;
                    let bx: &Box<CljCell> = vs.as_ref().unwrap();
                    let key: String = (*bx).to_string();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(&key);
                    ptr = MyMath::subtraction(ptr, rccell.clone());
                },
                _ => {},
            }
        }
        return ptr;
    }

    pub fn clj_list_subtraction(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {
        //let ptr:Rc<CljCell>  = MyMath::clj_list_subtraction1(interp, lst, func_hs, CljRecur, rs.clone());
        //return Rc::try_unwrap(ptr).unwrap();
        return MyMath::clj_list_subtraction1(interp, lst, func_hs, clj_recur, rs.clone());
    }

    pub fn mul(c: Rc<CljCell>, c1: Rc<CljCell>) -> Rc<CljCell> {
        match &*c {
            CljCell::IntClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::IntClj(m * m1));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::LongClj((*m as i64) * m1));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj((*m as f32) * m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) * m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::IntClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::LongClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::LongClj(m * (*m1 as i64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::LongClj(m * m1));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj((*m as f32) * m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) * m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::LongClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::DoubleClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m * (*m1 as f64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m * (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m * (*m1 as f64)));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj(m * m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::DoubleClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::FloatClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::FloatClj(m * (*m1 as f32)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) * (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::FloatClj(m * m1));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) * m1));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::FloatClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::Null => {
                return c1.clone();
            },
            _ => {},
        }
        return Rc::new(CljCell::Null);
    }
    /**
     * 加法
     * */
    pub fn clj_list_mul1(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {

        let mut ptr = Rc::new(CljCell::Null);
        for c in &lst[1..] {
            match &**c {
                CljCell::IntClj(m) => {
                    ptr = MyMath::mul(ptr, c.clone());
                },
                CljCell::LongClj(m) => {
                    ptr = MyMath::mul(ptr, c.clone());
                },
                CljCell::DoubleClj(m) => {
                    ptr = MyMath::mul(ptr, c.clone());
                },
                CljCell::FloatClj(m) => {
                    ptr = MyMath::mul(ptr, c.clone());
                },
                CljCell::Symbol(vs) => {
                    let key: Rc<String> = (**vs).clone().into();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(key.as_ref());
                    ptr = MyMath::mul(ptr, rccell.clone());
                },
                CljCell::CljList(vs) => {
                    let rs_cell = interp.clj_list(vs, func_hs, clj_recur, rs.clone());
                    ptr = MyMath::add(ptr, rs_cell.clone());
                },
                CljCell::Percent(bpx) => {
                    let vs: &Option<Box<CljCell>> = bpx;
                    let bx: &Box<CljCell> = vs.as_ref().unwrap();
                    let key: String = (*bx).to_string();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(&key);
                    ptr = MyMath::mul(ptr, rccell.clone());
                },
                _ => {},
            }
        }
        return ptr;
    }

    pub fn clj_list_mul(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {
        //let ptr:Rc<CljCell>  = MyMath::clj_list_mul1(interp, lst, func_hs, CljRecur, rs.clone());
        //return Rc::try_unwrap(ptr).unwrap();
        return MyMath::clj_list_mul1(interp, lst, func_hs, clj_recur, rs.clone());
    }

    pub fn div(c: Rc<CljCell>, c1: Rc<CljCell>) -> Rc<CljCell> {
        match &*c {
            CljCell::IntClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::DoubleClj(*m as f64));
                    },
                    _ => {},
                }
            },
            CljCell::LongClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::DoubleClj(*m as f64));
                    },
                    _ => {},
                }
            },
            CljCell::DoubleClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::DoubleClj(*m));
                    },
                    _ => {},
                }
            },
            CljCell::FloatClj(m) => {
                match &*c1 {
                    CljCell::IntClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::LongClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::FloatClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::DoubleClj(m1) => {
                        return Rc::new(CljCell::DoubleClj((*m as f64) / (*m1 as f64)));
                    },
                    CljCell::Null => {
                        return Rc::new(CljCell::DoubleClj(*m as f64));
                    },
                    _ => {},
                }
            },
            CljCell::Null => {
                return c1.clone();
            },
            _ => {},
        }
        return Rc::new(CljCell::Null);
    }
    /**
     * 加法
     * */
    pub fn clj_list_div1(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {
        //let x = Rc::new(CljCell::Null);
        //let mut ptr: *mut CljCell = Rc::into_raw(x) as *mut CljCell;
        //let x = Box::new(CljCell::Null);
        //let mut ptr: *mut CljCell = Box::into_raw(x);
        let mut ptr = Rc::new(CljCell::Null);
        for c in &lst[1..] {
            match &**c {
                CljCell::IntClj(m) => {
                    ptr = MyMath::div(ptr, c.clone());
                },
                CljCell::LongClj(m) => {
                    ptr = MyMath::div(ptr, c.clone());
                },
                CljCell::DoubleClj(m) => {
                    ptr = MyMath::div(ptr, c.clone());
                },
                CljCell::FloatClj(m) => {
                    ptr = MyMath::div(ptr, c.clone());
                },
                CljCell::Symbol(vs) => {
                    let key: Rc<String> = (**vs).clone().into();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(key.as_ref());
                    ptr = MyMath::div(ptr, rccell.clone());
                },
                CljCell::CljList(vs) => {
                    let rs_cell = interp.clj_list(vs, func_hs, clj_recur, rs.clone());
                    ptr = MyMath::add(ptr, rs_cell.clone());
                },
                CljCell::Percent(bpx) => {
                    let vs: &Option<Box<CljCell>> = bpx;
                    let bx: &Box<CljCell> = vs.as_ref().unwrap();
                    let key: String = (*bx).to_string();
                    //let fhs = func_hs.borrow();
                    let rccell: &Rc<CljCell> = func_hs.get(&key);
                    ptr = MyMath::div(ptr, rccell.clone());
                },
                _ => {},
            }
        }
        return ptr;
    }

    pub fn clj_list_div(interp: &MyClojureInterp, lst: &Vec<Rc<CljCell>>, func_hs: &MyFuncHs, clj_recur: &MyCljRecur, rs: Rc<CljCell>) -> Rc<CljCell> {
        //let ptr:Rc<CljCell>  = MyMath::clj_list_div1(interp, lst, func_hs, CljRecur, rs.clone());
        //return Rc::try_unwrap(ptr).unwrap();
        return MyMath::clj_list_div1(interp, lst, func_hs, clj_recur, rs.clone());
    }
}











































